package hasthi.actuators.actions;

import hasthi.actuators.ResourceBasedAction;
import hasthi.actuators.SystemConfig;
import hasthi.actuators.impl.HostAgentBasedHostProxy;
import hasthi.common.HasthiException;
import hasthi.common.HasthiException.FaultCode;
import hasthi.common.constants.HasthiConstants;
import hasthi.common.constants.WSDMConstants;
import hasthi.config.DeploymentConfig;
import hasthi.config.ResourceProfile;
import hasthi.container.MessageContext;
import hasthi.container.SimpleMessageContext;
import hasthi.container.SoapContainer;
import hasthi.container.UnicastContianier;
import hasthi.manager.ExecuteCommandDocument;
import hasthi.manager.ExecuteCommandDocument.ExecuteCommand;
import hasthi.metamodel.resource.ManagedResource;
import hasthi.tools.FailureDetectionTools;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.MalformedURLException;
import java.net.Socket;
import java.net.URL;
import java.net.UnknownHostException;

import javax.xml.namespace.QName;

import xsul.invoker.soap_over_http.SoapHttpDynamicInfosetInvoker;
import edu.indiana.extreme.wsdm.ShutDownRequestDocument;
import edu.indiana.extreme.wsdm.ShutDownRequestDocument.ShutDownRequest;

public class ShutDownAction extends ResourceBasedAction {
    private String name;

    public ShutDownAction(ManagedResource resource) {
        super(resource);
        name = ShutDownAction.class.getName() + "|" + resource.getName();
    }

    @Override
    public void execute() throws HasthiException {
        try {
            SystemConfig systemConfig = actionContext.getSystemConfig();
            ResourceProfile resourceProfile = systemConfig.getResourceProfile(QName
                    .valueOf(resource.getType()));
            if(resourceProfile == null 
                    && ManagedResource.ResourceType.Service.toString().equals(resource.getCategory())){
                throw new HasthiException("Resource Profile for the resource "+ resource.getType() + "not found ",
                        HasthiException.FaultCode.ConfigError);
            }

            DeploymentConfig deploymentConfig = resourceProfile != null ? resourceProfile
                    .getDeployment() : null;
            UnicastContianier container = actionContext.getUnicastContainer();

            String hostName = resource.getProperty(WSDMConstants.HOSTNAME_PARAM.getLocalPart());
            String stoppedResourceUrl = resource.getManagerAgentURL();
            boolean shutdownCompleted = false; 
            
            if (deploymentConfig != null && hostName != null) {
                ManagedResource deployHost = actionContext.getResourceModel().getResource(
                        new QName(hostName));
                if (deployHost != null) {
                    HostAgentBasedHostProxy remoteSystemProxy = new HostAgentBasedHostProxy(
                            actionContext.getBaseContext(), deployHost.getManagerAgentURL(),
                            container);
                    if (HasthiConstants.Actions.CONTAINER_TOMCAT.equals(deploymentConfig
                            .getContainer())) {
                        try{
                            ShutDownRequestDocument requestDocument = ShutDownRequestDocument.Factory.newInstance();
                            ShutDownRequest shutDownRequest = requestDocument.addNewShutDownRequest();
                            MessageContext message = new SimpleMessageContext(shutDownRequest, resource
                                    .getManagerAgentURL(), WSDMConstants.ACTION_SHUTDOWN);
                            container.sendReceive(message);
                        }catch(Exception e){
                            log.warn("Shutting down service failed, doing tomcat shutdown");
                        }
                        ExecuteCommandDocument commandDocument = ExecuteCommandDocument.Factory
                                .newInstance();
                        ExecuteCommand executeCommand = commandDocument.addNewExecuteCommand();
                        executeCommand.setWorkdir(deploymentConfig.getInstallDir());
                        executeCommand.setCmd(HasthiConstants.Actions.COMMAND_TOMCAT_STOP);
                        container.sendReceive(new SimpleMessageContext(commandDocument, deployHost
                                .getManagerAgentURL(), HasthiConstants.EXECUATE_COMMAND_ACTION));
                        
                        //if shutdown command is give for tomcat based resource we do that as well
                        //this will take care of tomcat running in the background
                        try {
                            if(deploymentConfig.getShutDownCommand() != null){
                                remoteSystemProxy.runCommand(hostName, deploymentConfig
                                        .getShutDownCommand(), null, deploymentConfig
                                        .getInstallDir(), false, "");
                            }
                        } catch (Exception e) {
                            log.error(e);
                        }
                        shutdownCompleted = true;
                    } else if (resourceProfile.getDeployment().getShutDownCommand() != null) {
                        String shutdownCmd = resourceProfile.getDeployment().getShutDownCommand();
                        if (shutdownCmd.startsWith("hasthi::")) {
                            String shutdownUrl = shutdownCmd.substring(8);
                            if (shutdownUrl.startsWith("tcp")) {
                                shutDownWitTcp(new URL(shutdownUrl));
                                stoppedResourceUrl = shutdownUrl;
                            } else if (shutdownUrl.startsWith("http")
                                    || shutdownUrl.startsWith("https")) {
                                shutdownWithHttp(shutdownUrl);
                                stoppedResourceUrl = shutdownUrl;
                            } else {
                                throw new HasthiException("Unknown command "+shutdownCmd, FaultCode.ConfigError);
                            }
                            shutdownCompleted = true;
                        }else {
                            remoteSystemProxy.runCommand(hostName, deploymentConfig
                                    .getShutDownCommand(), null, deploymentConfig
                                    .getInstallDir(), false, "");
                            stoppedResourceUrl = resource.getManagerAgentURL();
                            shutdownCompleted = true;
                        }
                    }
                }
            }
            if(!shutdownCompleted){
                ShutDownRequestDocument requestDocument = ShutDownRequestDocument.Factory.newInstance();
                ShutDownRequest shutDownRequest = requestDocument.addNewShutDownRequest();
                MessageContext message = new SimpleMessageContext(shutDownRequest, resource
                        .getManagerAgentURL(), WSDMConstants.ACTION_SHUTDOWN);
                container.sendReceive(message);
                stoppedResourceUrl = resource.getManagerAgentURL();
            }
            
            if(stoppedResourceUrl != null){
                if(FailureDetectionTools.isSocketUp(stoppedResourceUrl)){
                    throw new HasthiException("Shutting down resource "+ resource.getName() + " running in "+ resource.getManagerAgentURL() + " Failed",FaultCode.ConfigError);
                }
            }
            actionContext.getBaseContext().getDlogger().info("Resource "+resource.getName() + " Stopped");
        } catch (MalformedURLException e) {
            throw new HasthiException(e, FaultCode.ConfigError);
        }
    }

    private void shutDownWitTcp(URL url) throws HasthiException {
        try {
            Socket socket = new Socket(url.getHost(), url.getPort());
            if (socket.isConnected()) {
                PrintWriter pw = new PrintWriter(socket.getOutputStream(), true);
                pw.println(url.getPath());// send shut down command

                InputStream in = socket.getInputStream();

                byte[] data = new byte[in.available()];
                in.read(data);

                System.out.println(new String(data));
                pw.close();
                socket.close();
            }
        } catch (UnknownHostException e) {
            throw new HasthiException(e, FaultCode.InvaliedLocalArgumnet);
        } catch (IOException e) {
            throw new HasthiException(e, FaultCode.InternalServiceError);
        }
    }

    private void shutdownWithHttp(String url) throws HasthiException {
        SoapHttpDynamicInfosetInvoker invoker;
        if (url.startsWith("https")) {
            invoker = SoapContainer.createSecureInvoker(actionContext.getBaseContext(), null);
        } else {
            invoker = new SoapHttpDynamicInfosetInvoker();
        }
        String response = invoker.invokeHttpPost(url, null, null);
        System.out.println(response);
    }

    @Override
    public String getActionID() {
        return name;
    }

    @Override
    public String toString() {
        return name;
    }
}
